package com.lfy.ai.aop;

import com.lfy.ai.AiChatStandard;
import com.lfy.ai.AiStrategyFactory;
import com.lfy.ai.anno.Dispatching;
import com.lfy.modules.ai.cache.AiDispatchCache;
import com.lfy.modules.common.domain.R;
import com.lfy.modules.common.utils.SpElUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class AiDispatchingAspect {
    @Pointcut("@annotation(com.lfy.ai.anno.Dispatching)")
    public void dispatchingPointcut() {}
    @Resource
    private AiDispatchCache aiDispatchCache;
    @Resource
    private AiStrategyFactory aiStrategyFactory;

    @Around("dispatchingPointcut()")
    public Object dispatchingAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature me = (MethodSignature) joinPoint.getSignature();
        Method method = me.getMethod();
        Dispatching annotation = AnnotationUtils.getAnnotation(method, Dispatching.class);
        String modelIdEl = annotation.modelId();
        String messageEl = annotation.message();
        String modelTypeEl = annotation.modelType();
        String urlEl = annotation.url();
        Object[] args = joinPoint.getArgs();
        String modelId = SpElUtils.parseSpEl(method, args, modelIdEl);
        String message = SpElUtils.parseSpEl(method, args, messageEl);
        String modelType = SpElUtils.parseSpEl(method, args, modelTypeEl);
        String url = SpElUtils.parseSpEl(method, args, urlEl);

        if(modelId == null || message == null || modelType == null || url == null){
            log.error("el表达式解析失败 modelId:{}, message:{}, modelType:{}, url:{}" , modelId, message, modelType, url);
            return R.error("参数异常");
        }

        log.info("ai请求参数 modelId:{}, message:{}, modelType:{}, url:{}" , modelId, message, modelType, url);
        Integer usage = null;
        try {
             usage = calculateUsage(modelId, modelType, message);
        }catch (Exception e){
            log.error("in com.lfy.ai.aop.AiDispatchingAspect.dispatchingAround() 计算用量失败 modelId:{}", modelId, e);
            return R.error("调度计算失败");
        }

        if(usage == null){
            log.error("in com.lfy.ai.aop.AiDispatchingAspect.dispatchingAround() 获取用量失败 modelId:{}", modelId);
            return R.error("调度计算失败");
        }
        boolean isFilter = aiDispatchCache.isFilter(Integer.parseInt(modelId), Integer.parseInt(modelType), url, Long.parseLong(String.valueOf(usage)));
        if(isFilter){
            log.info("ai请求被过滤 modelId:{}, message:{}, modelType:{}, url:{}" , modelId, message, modelType, url);
            return R.error("此模型算力用量已超限");
        }

        return joinPoint.proceed();
    }
    /**
     * 计算调用用量
     */
    public Integer calculateUsage(String modelId,String modelType,String message){
        AiChatStandard strategy = aiStrategyFactory.getStrategy(Integer.parseInt(modelId));
        if(strategy == null){
            log.error("in com.lfy.ai.aop.AiDispatchingAspect.calculateUsage() AI策略不存在 modelId:{}", modelId);
            return null;
        }

        return strategy.tokenizer(message, Integer.parseInt(modelType));
    }

}
